package com.ybbase.framework.controller.flow.base;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ybbase.framework.base.model.ResponseBean;
import com.ybbase.framework.common.constant.CommonResultCode;
import com.ybbase.framework.common.util.DateUtil;
import com.ybbase.framework.model.po.flow.FlowConfig;
import com.ybbase.framework.model.po.flow.FlowModel;
import com.ybbase.framework.service.flow.FlowConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.NativeModelQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author cg
 * @date 2019-8-1
 * 流程模型管理
 * */
@RestController
@RequestMapping("/flowmodel")
@Api(value = "流程模型管理",tags = "流程模型管理")
public class FlowModelController {

    private static final Logger LOGGER = LoggerFactory.getLogger(FlowModelController.class);

    @Autowired
    ObjectMapper objectMapper;
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    IdentityService IndentityService;
    @Autowired
    ManagementService managementService;
    @Autowired
    FlowConfigService flowConfigService;

    /** 定义流程引擎 */
    @Resource
    private ProcessEngine processEngine;

    private final static Integer MODEL_REVISION = 1;
    /** modeler.html页面地址 */
    private final static String ACTIVITI_REDIRECT_MODELER_INDEX = "activitiModeler/modeler.html?modelId=";
    /** 默认的空间值 */
    private final static String ACTIVITI_NAMESPACE_VALUE = "http://b3mn.org/stencilset/bpmn2.0#";
    /** 默认ID值 */
    private final static String ACTIVITI_ID_VALUE = "canvas";

    /**
     * 新建或编辑流程模型
     * @author cg
     * @date 2019-08-15 10:20
     * @param response
     * @param modelId 流程模型编码，编辑时传递模型ID
     * @return
     * @throws
     * @since
     */
    @GetMapping("/create")
    @ApiOperation(value = "新建或编辑流程模型",notes="新建或编辑流程模型",response = ResponseBean.class)
    @ApiImplicitParam(name="modelId",value="流程模型编码，编辑时传递模型ID", dataType = "String")
    public ResponseBean createModel(String modelId,HttpServletResponse response) throws IOException {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        if(StringUtils.isNotEmpty(modelId)){
            return new ResponseBean(CommonResultCode.SUCCESS.getCode(),CommonResultCode.SUCCESS.getMsg(),modelId);
        }else{
            // 初始化空的流程资源模型，填充信息并持久化模型
            Model model = repositoryService.newModel();
            String uuid = UUID.randomUUID().toString();
            ObjectNode modelNode = objectMapper.createObjectNode();
            //设置一些默认信息
            /** 模型的名称 */
            modelNode.put(ModelDataJsonConstants.MODEL_NAME, uuid);
            /** 模型的描述 */
            modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, "");
            /** 模型的版本 */
            modelNode.put(ModelDataJsonConstants.MODEL_REVISION, MODEL_REVISION);
            model.setName(uuid);
            model.setKey(uuid);
            model.setMetaInfo(modelNode.toString());
            repositoryService.saveModel(model);
            //将 editorNode  数据填充到模型中, 并做页面的重定向
            ObjectNode editorNode = objectMapper.createObjectNode();
            editorNode.put("id", ACTIVITI_ID_VALUE);
            editorNode.put("resourceId", ACTIVITI_ID_VALUE);
            ObjectNode stencilSetNode = objectMapper.createObjectNode();
            stencilSetNode.put("namespace",ACTIVITI_NAMESPACE_VALUE);
            editorNode.put("stencilset", stencilSetNode);
            repositoryService.addModelEditorSource(model.getId(),editorNode.toString().getBytes(StandardCharsets.UTF_8));

            return new ResponseBean(CommonResultCode.SUCCESS.getCode(),CommonResultCode.SUCCESS.getMsg(),model.getId()  );
        }
    }

    /**
     * 获取所有模型
     * @author cg
     * @date 2019-08-15 10:43
     * @param
     * @return
     * @throws
     * @since
     */
    @GetMapping("/modelList")
    @ApiOperation(value = "获取所有模型",notes="获取所有模型",response = ResponseBean.class)
    @ApiImplicitParam(name="flow", value ="流程模型", required = true, dataType = "FlowModel")
    public ResponseBean modelList(FlowModel flow,@RequestParam(name = "current", defaultValue = "1") int pageIndex,
                                  @RequestParam(name = "size", defaultValue = "20") int rows){
        RepositoryService repositoryService = processEngine.getRepositoryService();
        String sql ="";
        if (StringUtils.isNotEmpty(flow.getFlowName())){
            sql += " AND h.NAME_ LIKE concat('%',#{modelName},'%')";
        }
        if (StringUtils.isNotEmpty(flow.getStartTime())){
            sql += " AND h.CREATE_TIME_ > #{startTime} ";
        }
        if (StringUtils.isNotEmpty(flow.getEndTime())){
            sql += " AND h.CREATE_TIME_ < #{endTime} ";
        }
        sql += " ORDER BY h.CREATE_TIME_ DESC ";
        NativeModelQuery nativeModelQueryList = repositoryService.createNativeModelQuery().sql("SELECT * FROM "+managementService.getTableName(Model.class)+" h WHERE 1=1 " + sql );
        NativeModelQuery nativeModelQueryCount = repositoryService.createNativeModelQuery().sql("SELECT count(*) FROM "+managementService.getTableName(Model.class)+" h WHERE 1=1 " + sql );
        if (StringUtils.isNotEmpty(flow.getFlowName())){
            nativeModelQueryList = nativeModelQueryList.parameter("modelName", "%"+flow.getFlowName()+"%");
            nativeModelQueryCount = nativeModelQueryCount.parameter("modelName", "%"+flow.getFlowName()+"%");
        }
        if (StringUtils.isNotEmpty(flow.getStartTime())){
            nativeModelQueryList = nativeModelQueryList.parameter("startTime", flow.getStartTime());
            nativeModelQueryCount = nativeModelQueryCount.parameter("startTime", flow.getStartTime());
        }
        if (StringUtils.isNotEmpty(flow.getEndTime())){
            nativeModelQueryList = nativeModelQueryList.parameter("endTime", flow.getEndTime());
            nativeModelQueryCount = nativeModelQueryCount.parameter("endTime", flow.getEndTime());
        }
        List<Model> modelList = nativeModelQueryList.listPage((pageIndex-1)*rows, rows);
        Long cnum = nativeModelQueryCount.count();
        List<FlowModel> list= new ArrayList<>();
        if (modelList!=null&&modelList.size()>0){
            for (Model model:modelList){
                FlowModel flowModel = new FlowModel();
                flowModel.setFlowId(model.getId());
                flowModel.setFlowKey(model.getKey());
                flowModel.setFlowName(model.getName());
                flowModel.setCreateTime(DateUtil.getTime(model.getCreateTime()!=null?model.getCreateTime():new Date()));
                flowModel.setLastUpdateTime(DateUtil.getTime(model.getLastUpdateTime()!=null?model.getLastUpdateTime():new Date()));
                Map<String,Object> stringMap = (Map<String,Object>)JSON.parse(model.getMetaInfo());
                flowModel.setRemarks(stringMap.get("description")+"");
                flowModel.setVersion(model.getVersion()+"");
                flowModel.setType(Integer.parseInt(StringUtils.isNotEmpty(model.getCategory())?model.getCategory():"0"));
                Deployment deployment =  repositoryService.createDeploymentQuery().deploymentId(StringUtils.isNotEmpty(model.getDeploymentId())?model.getDeploymentId():"0").singleResult();
                if(deployment!=null){
                    //已经部署
                    flowModel.setIsDeploy(1);
                }else {
                    //未部署
                    flowModel.setIsDeploy(0);
                }
                list.add(flowModel);
            }
        }
        Page<FlowModel> page = new Page(pageIndex,rows);
        page.setTotal(cnum);
        page.setCurrent(pageIndex);
        page.setSize(rows);
        page.setRecords(list);
        return new ResponseBean(CommonResultCode.SUCCESS.getCode(),CommonResultCode.SUCCESS.getMsg(),page);
    }

    /**
     * 发布模型为流程定义
     * @author cg
     * @date 2019-08-15 10:44
     * @param modelId 模型编码
     * @return
     * @throws
     * @since
     */
    @GetMapping("/deploy")
    @ApiOperation(value = "发布模型为流程定义",notes="发布模型为流程定义",response = ResponseBean.class)
    @ApiImplicitParam(name="modelId", value ="模型编码", required = true, dataType = "String")
    public ResponseBean deploy(String modelId) throws Exception {
        //获取模型
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Model modelData = repositoryService.getModel(modelId);
        Deployment deploymentOld =  repositoryService.createDeploymentQuery().deploymentId(StringUtils.isNotEmpty(modelData.getDeploymentId())?modelData.getDeploymentId():"0").singleResult();
        if(deploymentOld!=null){
            return new ResponseBean(CommonResultCode.FAIL.getCode(),"当前模型已经部署请勿重新部署",null);
        }else{
            byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
            if (bytes == null) {
                return new ResponseBean(CommonResultCode.FAIL.getCode(),"模型数据为空，请先设计流程并成功保存，再进行发布。",null);
            }
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if(model.getProcesses().size()==0){
                return new ResponseBean(CommonResultCode.FAIL.getCode(),"数据模型不符要求，请至少设计一条主线流程。",null);
            }
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
            //发布流程
            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
            return new ResponseBean(CommonResultCode.SUCCESS.getCode(),"部署"+CommonResultCode.SUCCESS.getMsg(),null);
        }

    }

    /**
     * 删除流程模型
     * @author cg
     * @date 2019-08-15 10:44
     * @param modelId 模型Id
     * @return
     * @throws
     * @since
     */
    @DeleteMapping("/removeModel")
    @ApiOperation(value = "删除流程模型",notes="删除流程模型",response = ResponseBean.class)
    @ApiImplicitParam(name="modelId", value ="模型Id", required = true, dataType = "String")
    public ResponseBean removeModel(String modelId, HttpServletRequest request) {
        if(!request.getMethod().equals("OPTIONS")){
            RepositoryService repositoryService = processEngine.getRepositoryService();
            Model model = repositoryService.createModelQuery().modelId(modelId).singleResult();
            List<Deployment> deployments  = repositoryService.createDeploymentQuery().deploymentId(StringUtils.isNotEmpty(model.getDeploymentId())?model.getDeploymentId():"0").list();
            try{
                if(deployments!=null&&!deployments.isEmpty()){
                    return new ResponseBean(CommonResultCode.FAIL.getCode(),"当前有进行的实例不能进行删除",null);
                }else{
                    FlowConfig flowConfig = new FlowConfig();
                    flowConfig.setModelCode(modelId);
                    repositoryService.deleteModel(modelId);
                    QueryWrapper<FlowConfig> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda().eq(FlowConfig::getModelCode, modelId);
                    flowConfigService.remove(queryWrapper);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return new ResponseBean(CommonResultCode.SUCCESS.getCode(),"删除"+CommonResultCode.SUCCESS.getMsg(),null);
    }

    /**
     * 删除流程部署信息
     * @author cg
     * @date 2019-08-15 10:44
     * @param modelId 模型Id
     * @return
     * @throws
     * @since
     */
    @GetMapping("/removeProcess")
    @ApiOperation(value = "删除流程部署信息",notes="删除流程部署信息",response = ResponseBean.class)
    @ApiImplicitParam(name="modelId", value ="模型Id", required = true, dataType = "String")
    public ResponseBean removeProcess(String modelId) {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Model model = repositoryService.createModelQuery().modelId(modelId).singleResult();
        List<ProcessInstance> list=runtimeService.createProcessInstanceQuery().deploymentId(StringUtils.isNotEmpty(model.getDeploymentId())?model.getDeploymentId():"0").list();
        if(list!=null&&list.size()>0){
            return new ResponseBean(CommonResultCode.FAIL.getCode(),"当前有进行的实例不能进行停止",null);
        }else{
            if(StringUtils.isNotEmpty(model.getDeploymentId())){
                repositoryService.deleteDeployment(model.getDeploymentId());
            }
        }
        return new ResponseBean(CommonResultCode.SUCCESS.getCode(),"停止流程部署"+CommonResultCode.SUCCESS.getMsg(),null);
    }
}
